#ifndef SNACK_EVENT_IMPL_H
#define SNACK_EVENT_IMPL_H

#include "TrackImplementation.h"
#include "FMODSystem.h"

#include <string>
#include <iostream>

namespace SnackBreak
{
namespace Impl
{

class FMODMixEvent : public MixEvent
{
public:
	// the way the mix event is triggered
	enum TriggerType
	{
		FirstMixType = 0,
		EndOfTrack = 0, 
		SyncPoint = 1,
		DSP = 1,
	};

	// Generation of unique names for events (needed by callback implementation).
	static std::string GenerateName(TriggerType t);

	// accessors
	const TriggerType& getTriggerType() { return triggerType; };
	const std::string& getName() { return name; };
	const double getEventLength() { return eventLength; }; // length of mix, in beats
	const double getStartBeat() { return eventStartBeat; }; // length of mix, in beats
	const double getEndBeat() { return eventStartBeat + eventLength; }; 
	FMODTrack* Outgoing() { return outgoing; };
	FMODTrack* Incoming() { return incoming; };

	// utility
	void PitchAndCueIncoming(double cueBeatNum = 0.0);
	double CalculateFudgeAmount();

	~FMODMixEvent() {};

protected:
	FMODMixEvent(FMODTrack* o, FMODTrack* i, TriggerType t)
	:
		outgoing(o),
		incoming(i),
		triggerType(t),
		eventLength(0.0),
		eventStartBeat(0.0),
		eventCueBeat(0.0)
	{
		name = GenerateName(triggerType);
	};

	FMODTrack* outgoing;
	FMODTrack* incoming;

	TriggerType triggerType;
	std::string name;
	double eventLength; // length of mix in beats
	double eventStartBeat; // start beat of mix (in outgoing)
	double eventCueBeat; // start beat of mix (in incoming)
};

// SyncEvent interface
class SyncEvent : public FMODMixEvent
{
protected:
	SyncEvent(FMODTrack* o, FMODTrack* i, TriggerType t)
	:
		FMODMixEvent(o, i, t)
	{

	};

	// in here, Sync Events implement what happens
	virtual void SyncAction() = 0;

	// the callback thing implementing the transport operations (e.g. Play, Pause) for this mix (and every other mix)
	static FMOD_RESULT F_CALLBACK SyncCallback(FMOD_CHANNEL *  channel, FMOD_CHANNEL_CALLBACKTYPE  type, int  command, unsigned int  commanddata1, unsigned int  commanddata2);
};

// AutomationEvent interface
class AutomationEvent : public FMODMixEvent
{
public:
	typedef enum 
	{
		Waiting = 0,
		Happening = 1,
		Finished = 2,
	} AutomationStage;

	~AutomationEvent();

	// this gets called lots during the DSP callback, implementer does stuff to the tracks 
	// based on current mix position (i.e. time)
	virtual void AutomationAction(double bufferLengthSec) = 0;

protected:
	AutomationEvent(FMODTrack* o, FMODTrack* i);
	
	// Common method to bump stage. Returns true if the stage required changing, otherwise false.
	bool UpdateStage(double bufferDuration);

protected:
	AutomationStage stage;
};

// Simplest mix ever - when outgoing, finishes, start incoming playing.
class DropEvent : public SyncEvent
{
public:
	DropEvent(FMODTrack* o, FMODTrack* i);

	virtual void SyncAction();
};

// Next stupidest mix - match the beats of the two tracks and drop 
// incoming numBeats before outgoing's last beat. (i.e. numBeats == mix len)
// Does not do any crossfading.
class OverlapMixEvent : public SyncEvent
{
public:
	OverlapMixEvent(FMODTrack* o, FMODTrack* i, double numBeats);

	virtual void SyncAction();

protected:
	void Setup();
};

// Overlap with a linear crossfade. At last, something resembling a real mix.
// this is a new base ish class
// "crossfademixevent" (i.e. has two channels' faders automated)
// this is bringing home th point about modularity in this event hierarchy - 
// cos at the moment, my approach is to "hard-code" this as a 2-fader thing
// (rather than implement a general fader event and have 2 of them)
class BlendMixEvent : public AutomationEvent
{
public:
	BlendMixEvent(FMODTrack* o, FMODTrack* i, double numBeats, double mixLen, double incoCueAt);
	~BlendMixEvent() {};

	virtual void AutomationAction(double bufferDuration);
};

}; // namespace Impl
}; // namespace SnackBreak

#endif // SNACK_EVENT_IMPL_H
